home *** CD-ROM | disk | FTP | other *** search
/ PC PowerPlay 58 / pcpp58a.iso / extras / quake 3 source / Q3A_ToolSource.exe / Main / q3asm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-02  |  20.3 KB  |  1,017 lines

  1.  
  2. #include "cmdlib.h"
  3. #include "mathlib.h"
  4. #include "qfiles.h"
  5.  
  6. char    outputFilename[MAX_OS_PATH];
  7.  
  8. // the zero page size is just used for detecting run time faults
  9. #define    ZERO_PAGE_SIZE    0        // 256
  10.  
  11. typedef enum {
  12.     OP_UNDEF, 
  13.  
  14.     OP_IGNORE, 
  15.  
  16.     OP_BREAK, 
  17.  
  18.     OP_ENTER,
  19.     OP_LEAVE,
  20.     OP_CALL,
  21.     OP_PUSH,
  22.     OP_POP,
  23.  
  24.     OP_CONST,
  25.     OP_LOCAL,
  26.  
  27.     OP_JUMP,
  28.  
  29.     //-------------------
  30.  
  31.     OP_EQ,
  32.     OP_NE,
  33.  
  34.     OP_LTI,
  35.     OP_LEI,
  36.     OP_GTI,
  37.     OP_GEI,
  38.  
  39.     OP_LTU,
  40.     OP_LEU,
  41.     OP_GTU,
  42.     OP_GEU,
  43.  
  44.     OP_EQF,
  45.     OP_NEF,
  46.  
  47.     OP_LTF,
  48.     OP_LEF,
  49.     OP_GTF,
  50.     OP_GEF,
  51.  
  52.     //-------------------
  53.  
  54.     OP_LOAD1,
  55.     OP_LOAD2,
  56.     OP_LOAD4,
  57.     OP_STORE1,
  58.     OP_STORE2,
  59.     OP_STORE4,                // *(stack[top-1]) = stack[yop
  60.     OP_ARG,
  61.     OP_BLOCK_COPY,
  62.  
  63.     //-------------------
  64.  
  65.     OP_SEX8,
  66.     OP_SEX16,
  67.  
  68.     OP_NEGI,
  69.     OP_ADD,
  70.     OP_SUB,
  71.     OP_DIVI,
  72.     OP_DIVU,
  73.     OP_MODI,
  74.     OP_MODU,
  75.     OP_MULI,
  76.     OP_MULU,
  77.  
  78.     OP_BAND,
  79.     OP_BOR,
  80.     OP_BXOR,
  81.     OP_BCOM,
  82.  
  83.     OP_LSH,
  84.     OP_RSHI,
  85.     OP_RSHU,
  86.  
  87.     OP_NEGF,
  88.     OP_ADDF,
  89.     OP_SUBF,
  90.     OP_DIVF,
  91.     OP_MULF,
  92.  
  93.     OP_CVIF,
  94.     OP_CVFI
  95. } opcode_t;
  96.  
  97. typedef struct {
  98.     int        imageBytes;        // after decompression
  99.     int        entryPoint;
  100.     int        stackBase;
  101.     int        stackSize;
  102. } executableHeader_t;
  103.  
  104. typedef enum {
  105.     CODESEG,
  106.     DATASEG,    // initialized 32 bit data, will be byte swapped
  107.     LITSEG,        // strings
  108.     BSSSEG,        // 0 filled
  109.     NUM_SEGMENTS
  110. } segmentName_t;
  111.  
  112. #define    MAX_IMAGE    0x400000
  113.  
  114. typedef struct {
  115.     byte    image[MAX_IMAGE];
  116.     int        imageUsed;
  117.     int        segmentBase;        // only valid on second pass
  118. } segment_t;
  119.  
  120. typedef struct symbol_s {
  121.     struct    symbol_s    *next;
  122.     int        hash;
  123.     segment_t    *segment;
  124.     char    *name;
  125.     int        value;
  126. } symbol_t;
  127.  
  128.  
  129. segment_t    segment[NUM_SEGMENTS];
  130. segment_t    *currentSegment;
  131.  
  132. int        passNumber;
  133.  
  134. int        numSymbols;
  135. int        errorCount;
  136.  
  137. symbol_t    *symbols;
  138. symbol_t    *lastSymbol;
  139.  
  140.  
  141. #define    MAX_ASM_FILES    256
  142. int        numAsmFiles;
  143. char    *asmFiles[MAX_ASM_FILES];
  144. char    *asmFileNames[MAX_ASM_FILES];
  145.  
  146. int        currentFileIndex;
  147. char    *currentFileName;
  148. int        currentFileLine;
  149.  
  150. //int        stackSize = 16384;
  151. int        stackSize = 0x10000;
  152.  
  153. // we need to convert arg and ret instructions to
  154. // stores to the local stack frame, so we need to track the
  155. // characteristics of the current functions stack frame
  156. int        currentLocals;            // bytes of locals needed by this function
  157. int        currentArgs;            // bytes of largest argument list called from this function
  158. int        currentArgOffset;        // byte offset in currentArgs to store next arg, reset each call
  159.  
  160. #define    MAX_LINE_LENGTH    1024
  161. char    lineBuffer[MAX_LINE_LENGTH];
  162. int        lineParseOffset;
  163. char    token[MAX_LINE_LENGTH];
  164.  
  165. int        instructionCount;
  166.  
  167. typedef struct {
  168.     char    *name;
  169.     int        opcode;
  170. } sourceOps_t;
  171.  
  172. sourceOps_t        sourceOps[] = {
  173. #include "opstrings.h"
  174. };
  175.  
  176. #define    NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) )
  177.  
  178. int        opcodesHash[ NUM_SOURCE_OPS ];
  179.  
  180.  
  181. /*
  182. =============
  183. HashString
  184. =============
  185. */
  186. int    HashString( char *s ) {
  187.     int        v;
  188.  
  189.     while ( *s ) {
  190.         v += *s;
  191.         s++;
  192.     }
  193.     return v;
  194. }
  195.  
  196.  
  197. /*
  198. ============
  199. CodeError
  200. ============
  201. */
  202. void CodeError( char *fmt, ... ) {
  203.     va_list        argptr;
  204.  
  205.     errorCount++;
  206.  
  207.     printf( "%s:%i ", currentFileName, currentFileLine );
  208.  
  209.     va_start( argptr,fmt );
  210.     vprintf( fmt,argptr );
  211.     va_end( argptr );
  212. }
  213.  
  214. /*
  215. ============
  216. EmitByte
  217. ============
  218. */
  219. void EmitByte( segment_t *seg, int v ) {
  220.     if ( seg->imageUsed >= MAX_IMAGE ) {
  221.         Error( "MAX_IMAGE" );
  222.     }
  223.     seg->image[ seg->imageUsed ] = v;
  224.     seg->imageUsed++;
  225. }
  226.  
  227. /*
  228. ============
  229. EmitInt
  230. ============
  231. */
  232. void EmitInt( segment_t *seg, int v ) {
  233.     if ( seg->imageUsed >= MAX_IMAGE - 4) {
  234.         Error( "MAX_IMAGE" );
  235.     }
  236.     seg->image[ seg->imageUsed ] = v & 255;
  237.     seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255;
  238.     seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255;
  239.     seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255;
  240.     seg->imageUsed += 4;
  241. }
  242.  
  243. /*
  244. ============
  245. DefineSymbol
  246.  
  247. Symbols can only be defined on pass 0
  248. ============
  249. */
  250. void DefineSymbol( char *sym, int value ) {
  251.     symbol_t    *s, *after;
  252.     char        expanded[MAX_LINE_LENGTH];
  253.     int            hash;
  254.  
  255.     if ( passNumber == 1 ) {
  256.         return;
  257.     }
  258.  
  259.     // add the file prefix to local symbols to guarantee unique
  260.     if ( sym[0] == '$' ) {
  261.         sprintf( expanded, "%s_%i", sym, currentFileIndex );
  262.         sym = expanded;
  263.     }
  264.  
  265.     hash = HashString( sym );
  266.  
  267.     for ( s = symbols ; s ; s = s->next ) {
  268.         if ( hash == s->hash && !strcmp( sym, s->name ) ) {
  269.             CodeError( "Multiple definitions for %s\n", sym );
  270.             return;
  271.         }
  272.     }
  273.  
  274.     s = malloc( sizeof( *s ) );
  275.     s->name = copystring( sym );
  276.     s->hash = hash;
  277.     s->value = value;
  278.     s->segment = currentSegment;
  279.  
  280.     lastSymbol = s;    /* for the move-to-lit-segment byteswap hack */
  281.  
  282.     // insert it in order
  283.     if ( !symbols || s->value < symbols->value ) {
  284.         s->next = symbols;
  285.         symbols = s;
  286.         return;
  287.     }
  288.  
  289.     for ( after = symbols ; after->next && after->next->value < value ; after = after->next ) {
  290.     }
  291.     s->next = after->next;
  292.     after->next = s;
  293. }
  294.  
  295.  
  296. /*
  297. ============
  298. LookupSymbol
  299.  
  300. Symbols can only be evaluated on pass 1
  301. ============
  302. */
  303. int LookupSymbol( char *sym ) {
  304.     symbol_t    *s;
  305.     char        expanded[MAX_LINE_LENGTH];
  306.     int            hash;
  307.  
  308.     if ( passNumber == 0 ) {
  309.         return 0;
  310.     }
  311.  
  312.     // add the file prefix to local symbols to guarantee unique
  313.     if ( sym[0] == '$' ) {
  314.         sprintf( expanded, "%s_%i", sym, currentFileIndex );
  315.         sym = expanded;
  316.     }
  317.  
  318.     hash = HashString( sym );
  319.     for ( s = symbols ; s ; s = s->next ) {
  320.         if ( hash == s->hash && !strcmp( sym, s->name ) ) {
  321.             return s->segment->segmentBase + s->value;
  322.         }
  323.     }
  324.  
  325.     CodeError( "ERROR: symbol %s undefined\n", sym );
  326.     passNumber = 0;
  327.     DefineSymbol( sym, 0 );    // so more errors aren't printed
  328.     passNumber = 1;
  329.     return 0;
  330. }
  331.  
  332.  
  333. /*
  334. ==============
  335. ExtractLine
  336.  
  337. Extracts the next line from the given text block.
  338. If a full line isn't parsed, returns NULL
  339. Otherwise returns the updated parse pointer
  340. ===============
  341. */
  342. char *ExtractLine( char *data ) {
  343.     int            i;
  344.  
  345.     currentFileLine++;
  346.     lineParseOffset = 0;
  347.     token[0] = 0;
  348.  
  349.     if ( data[0] == 0 ) {
  350.         lineBuffer[0] = 0;
  351.         return NULL;
  352.     }
  353.  
  354.     for ( i = 0 ; i < MAX_LINE_LENGTH ; i++ ) {
  355.         if ( data[i] == 0 || data[i] == '\n' ) {
  356.             break;
  357.         }
  358.     }
  359.     if ( i == MAX_LINE_LENGTH ) {
  360.         CodeError( "MAX_LINE_LENGTH" );
  361.         return data;
  362.     }
  363.     memcpy( lineBuffer, data, i );
  364.     lineBuffer[i] = 0;
  365.     data += i;
  366.     if ( data[0] == '\n' ) {
  367.         data++;
  368.     }
  369.     return data;
  370. }
  371.  
  372.  
  373. /*
  374. ==============
  375. Parse
  376.  
  377. Parse a token out of linebuffer
  378. ==============
  379. */
  380. qboolean Parse( void ) {
  381.     int        c;
  382.     int        len;
  383.     
  384.     len = 0;
  385.     token[0] = 0;
  386.     
  387.     // skip whitespace
  388.     while ( lineBuffer[ lineParseOffset ] <= ' ' ) {
  389.         if ( lineBuffer[ lineParseOffset ] == 0 ) {
  390.             return qfalse;
  391.         }
  392.         lineParseOffset++;
  393.     }
  394.  
  395.     // skip ; comments
  396.     c = lineBuffer[ lineParseOffset ];
  397.     if ( c == ';' ) {
  398.         return qfalse;
  399.     }
  400.     
  401.  
  402.     // parse a regular word
  403.     do {
  404.         token[len] = c;
  405.         len++;
  406.         lineParseOffset++;
  407.         c = lineBuffer[ lineParseOffset ];
  408.     } while (c>32);
  409.     
  410.     token[len] = 0;
  411.     return qtrue;
  412. }
  413.  
  414.  
  415. /*
  416. ==============
  417. ParseValue
  418. ==============
  419. */
  420. int    ParseValue( void ) {
  421.     Parse();
  422.     return atoi( token );
  423. }
  424.  
  425.  
  426. /*
  427. ==============
  428. ParseExpression
  429. ==============
  430. */
  431. int    ParseExpression(void) {
  432.     int        i, j;
  433.     char    sym[MAX_LINE_LENGTH];
  434.     int        v;
  435.  
  436.     if ( token[0] == '-' ) {
  437.         i = 1;
  438.     } else {
  439.         i = 0;
  440.     }
  441.  
  442.     for ( ; i < MAX_LINE_LENGTH ; i++ ) {
  443.         if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) {
  444.             break;
  445.         }
  446.     }
  447.  
  448.     memcpy( sym, token, i );
  449.     sym[i] = 0;
  450.  
  451.     if ( ( sym[0] >= '0' && sym[0] <= '9' ) || sym[0] == '-' ) {
  452.         v = atoi( sym );
  453.     } else {
  454.         v = LookupSymbol( sym );
  455.     }
  456.  
  457.     // parse add / subtract offsets
  458.     while ( token[i] != 0 ) {
  459.         for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) {
  460.             if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) {
  461.                 break;
  462.             }
  463.         }
  464.  
  465.         memcpy( sym, token+i+1, j-i-1 );
  466.         sym[j-i-1] = 0;
  467.  
  468.         if ( token[i] == '+' ) {
  469.             v += atoi( sym );
  470.         }
  471.         if ( token[i] == '-' ) {
  472.             v -= atoi( sym );
  473.         }
  474.         i = j;
  475.     }
  476.  
  477.     return v;
  478. }
  479.  
  480.  
  481. /*
  482. ==============
  483. HackToSegment
  484.  
  485. BIG HACK: I want to put all 32 bit values in the data
  486. segment so they can be byte swapped, and all char data in the lit
  487. segment, but switch jump tables are emited in the lit segment and
  488. initialized strng variables are put in the data segment.
  489.  
  490. I can change segments here, but I also need to fixup the
  491. label that was just defined
  492.  
  493. Note that the lit segment is read-write in the VM, so strings
  494. aren't read only as in some architectures.
  495. ==============
  496. */
  497. void HackToSegment( segmentName_t seg ) {
  498.     if ( currentSegment == &segment[seg] ) {
  499.         return;
  500.     }
  501.  
  502.     currentSegment = &segment[seg];
  503.     if ( passNumber == 0 ) {
  504.         lastSymbol->segment = currentSegment;
  505.         lastSymbol->value = currentSegment->imageUsed;
  506.     }
  507. }
  508.  
  509. /*
  510. ==============
  511. AssembleLine
  512.  
  513. ==============
  514. */
  515. void AssembleLine( void ) {
  516.     int        v, v2;
  517.     int        i;
  518.     int        hash;
  519.  
  520.     Parse();
  521.     if ( !token[0] ) {
  522.         return;
  523.     }
  524.  
  525.     hash = HashString( token );
  526.  
  527.     for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
  528.         if ( hash == opcodesHash[i] && !strcmp( token, sourceOps[i].name ) ) {
  529.             int        opcode;
  530.             int        expression;
  531.  
  532.             if ( sourceOps[i].opcode == OP_UNDEF ) {
  533.                 CodeError( "Undefined opcode: %s\n", token );
  534.             }
  535.             if ( sourceOps[i].opcode == OP_IGNORE ) {
  536.                 return;        // we ignore most conversions
  537.             }
  538.  
  539.             // sign extensions need to check next parm
  540.             opcode = sourceOps[i].opcode;
  541.             if ( opcode == OP_SEX8 ) {
  542.                 Parse();
  543.                 if ( token[0] == '1' ) {
  544.                     opcode = OP_SEX8;
  545.                 } else if ( token[0] == '2' ) {
  546.                     opcode = OP_SEX16;
  547.                 } else {
  548.                     CodeError( "Bad sign extension: %s\n", token );
  549.                     return;
  550.                 }
  551.             }
  552.  
  553.             // check for expression
  554.             Parse();
  555.             if ( token[0] && sourceOps[i].opcode != OP_CVIF
  556.                     && sourceOps[i].opcode != OP_CVFI ) {
  557.                 expression = ParseExpression();
  558.  
  559.                 // code like this can generate non-dword block copies:
  560.                 // auto char buf[2] = " ";
  561.                 // we are just going to round up.  This might conceivably
  562.                 // be incorrect if other initialized chars follow.
  563.                 if ( opcode == OP_BLOCK_COPY ) {
  564.                     expression = ( expression + 3 ) & ~3;
  565.                 }
  566.  
  567.                 EmitByte( &segment[CODESEG], opcode );
  568.                 EmitInt( &segment[CODESEG], expression );
  569.             } else {
  570.                 EmitByte( &segment[CODESEG], opcode );
  571.             }
  572.  
  573.             instructionCount++;
  574.             return;
  575.         }
  576.     }
  577.  
  578.     // call instructions reset currentArgOffset
  579.     if ( !strncmp( token, "CALL", 4 ) ) {
  580.         EmitByte( &segment[CODESEG], OP_CALL );
  581.         instructionCount++;
  582.         currentArgOffset = 0;
  583.         return;
  584.     }
  585.  
  586.     // arg is converted to a reversed store
  587.     if ( !strncmp( token, "ARG", 3 ) ) {
  588.         EmitByte( &segment[CODESEG], OP_ARG );
  589.         instructionCount++;
  590.         if ( 8 + currentArgOffset >= 256 ) {
  591.             CodeError( "currentArgOffset >= 256" );
  592.             return;
  593.         }
  594.         EmitByte( &segment[CODESEG], 8 + currentArgOffset );
  595.         currentArgOffset += 4;
  596.         return;
  597.     }
  598.  
  599.     // ret just leaves something on the op stack
  600.     if ( !strncmp( token, "RET", 3 ) ) {
  601.         EmitByte( &segment[CODESEG], OP_LEAVE );
  602.         instructionCount++;
  603.         EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  604.         return;
  605.     }
  606.  
  607.     // pop is needed to discard the return value of 
  608.     // a function
  609.     if ( !strncmp( token, "pop", 3 ) ) {
  610.         EmitByte( &segment[CODESEG], OP_POP );
  611.         instructionCount++;
  612.         return;
  613.     }
  614.  
  615.     // address of a parameter is converted to OP_LOCAL
  616.     if ( !strncmp( token, "ADDRF", 5 ) ) {
  617.         instructionCount++;
  618.         Parse();
  619.         v = ParseExpression();
  620.         v = 16 + currentArgs + currentLocals + v;
  621.         EmitByte( &segment[CODESEG], OP_LOCAL );
  622.         EmitInt( &segment[CODESEG], v );
  623.         return;
  624.     }
  625.  
  626.     // address of a local is converted to OP_LOCAL
  627.     if ( !strncmp( token, "ADDRL", 5 ) ) {
  628.         instructionCount++;
  629.         Parse();
  630.         v = ParseExpression();
  631.         v = 8 + currentArgs + v;
  632.         EmitByte( &segment[CODESEG], OP_LOCAL );
  633.         EmitInt( &segment[CODESEG], v );
  634.         return;
  635.     }
  636.  
  637.     if ( !strcmp( token, "proc" ) ) {
  638.         char    name[1024];
  639.  
  640.         Parse();                    // function name
  641.         strcpy( name, token );
  642.  
  643.         DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed );
  644.  
  645.         currentLocals = ParseValue();    // locals
  646.         currentLocals = ( currentLocals + 3 ) & ~3;
  647.         currentArgs = ParseValue();        // arg marshalling
  648.         currentArgs = ( currentArgs + 3 ) & ~3;
  649.  
  650.         if ( 8 + currentLocals + currentArgs >= 32767 ) {
  651.             CodeError( "Locals > 32k in %s\n", name );
  652.         }
  653.  
  654.         instructionCount++;
  655.         EmitByte( &segment[CODESEG], OP_ENTER );
  656.         EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  657.         return;
  658.     }
  659.     if ( !strcmp( token, "endproc" ) ) {
  660.         Parse();                // skip the function name
  661.         v = ParseValue();        // locals
  662.         v2 = ParseValue();        // arg marshalling
  663.  
  664.         // all functions must leave something on the opstack
  665.         instructionCount++;
  666.         EmitByte( &segment[CODESEG], OP_PUSH );
  667.  
  668.         instructionCount++;
  669.         EmitByte( &segment[CODESEG], OP_LEAVE );
  670.         EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  671.  
  672.         return;
  673.     }
  674.  
  675.  
  676.     if ( !strcmp( token, "address" ) ) {
  677.         Parse();
  678.         v = ParseExpression();
  679.  
  680.         HackToSegment( DATASEG );
  681.         EmitInt( currentSegment, v );
  682.         return;
  683.     }
  684.     if ( !strcmp( token, "export" ) ) {
  685.         return;
  686.     }
  687.     if ( !strcmp( token, "import" ) ) {
  688.         return;
  689.     }
  690.     if ( !strcmp( token, "code" ) ) {
  691.         currentSegment = &segment[CODESEG];
  692.         return;
  693.     }
  694.     if ( !strcmp( token, "bss" ) ) {
  695.         currentSegment = &segment[BSSSEG];
  696.         return;
  697.     }
  698.     if ( !strcmp( token, "data" ) ) {
  699.         currentSegment = &segment[DATASEG];
  700.         return;
  701.     }
  702.     if ( !strcmp( token, "lit" ) ) {
  703.         currentSegment = &segment[LITSEG];
  704.         return;
  705.     }
  706.     if ( !strcmp( token, "line" ) ) {
  707.         return;
  708.     }
  709.     if ( !strcmp( token, "file" ) ) {
  710.         return;
  711.     }
  712.  
  713.     if ( !strcmp( token, "equ" ) ) {
  714.         char    name[1024];
  715.  
  716.         Parse();
  717.         strcpy( name, token );
  718.         Parse();
  719.         DefineSymbol( name, atoi(token) );
  720.         return;
  721.     }
  722.  
  723.     if ( !strcmp( token, "align" ) ) {
  724.         v = ParseValue();
  725.         currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 );
  726.         return;
  727.     }
  728.  
  729.     if ( !strcmp( token, "skip" ) ) {
  730.         v = ParseValue();
  731.         currentSegment->imageUsed += v;
  732.         return;
  733.     }
  734.  
  735.     if ( !strcmp( token, "byte" ) ) {
  736.         v = ParseValue();
  737.         v2 = ParseValue();
  738.  
  739.         if ( v == 1 ) {
  740.             HackToSegment( LITSEG );
  741.         } else if ( v == 4 ) {
  742.             HackToSegment( DATASEG );
  743.         } else if ( v == 2 ) {
  744.             CodeError( "16 bit initialized data not supported" );
  745.         }
  746.  
  747.         // emit little endien
  748.         for ( i = 0 ; i < v ; i++ ) {
  749.             EmitByte( currentSegment, v2 );
  750.             v2 >>= 8;
  751.         }
  752.         return;
  753.     }
  754.  
  755.     // code labels are emited as instruction counts, not byte offsets,
  756.     // because the physical size of the code will change with
  757.     // different run time compilers and we want to minimize the
  758.     // size of the required translation table
  759.     if ( !strncmp( token, "LABEL", 5 ) ) {
  760.         Parse();
  761.         if ( currentSegment == &segment[CODESEG] ) {
  762.             DefineSymbol( token, instructionCount );
  763.         } else {
  764.             DefineSymbol( token, currentSegment->imageUsed );
  765.         }
  766.         return;
  767.     }
  768.  
  769.     CodeError( "Unknown token: %s\n", token );
  770. }
  771.  
  772. /*
  773. ==============
  774. InitTables
  775. ==============
  776. */
  777. void InitTables( void ) {
  778.     int        i;
  779.  
  780.     for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
  781.         opcodesHash[i] = HashString( sourceOps[i].name );
  782.     }
  783. }
  784.  
  785.  
  786. /*
  787. ==============
  788. WriteMapFile
  789. ==============
  790. */
  791. void WriteMapFile( void ) {
  792.     FILE        *f;
  793.     symbol_t    *s;
  794.     char        imageName[MAX_OS_PATH];
  795.     int            seg;
  796.  
  797.     strcpy( imageName, outputFilename );
  798.     StripExtension( imageName );
  799.     strcat( imageName, ".map" );
  800.  
  801.     printf( "Writing %s...\n", imageName );
  802.     f = SafeOpenWrite( imageName );
  803.     for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) {
  804.         for ( s = symbols ; s ; s = s->next ) {
  805.             if ( s->name[0] == '$' ) {
  806.                 continue;    // skip locals
  807.             }
  808.             if ( &segment[seg] != s->segment ) {
  809.                 continue;
  810.             }
  811.             fprintf( f, "%i %8x %s\n", seg, s->value, s->name );
  812.         }
  813.     }
  814.     fclose( f );
  815. }
  816.  
  817. /*
  818. ===============
  819. WriteVmFile
  820. ===============
  821. */
  822. void WriteVmFile( void ) {
  823.     char    imageName[MAX_OS_PATH];
  824.     vmHeader_t    header;
  825.     FILE    *f;
  826.  
  827.     printf( "%i total errors\n", errorCount );
  828.     strcpy( imageName, outputFilename );
  829.     StripExtension( imageName );
  830.     strcat( imageName, ".qvm" );
  831.  
  832.     remove( imageName );
  833.  
  834.     printf( "code segment: %7i\n", segment[CODESEG].imageUsed );
  835.     printf( "data segment: %7i\n", segment[DATASEG].imageUsed );
  836.     printf( "lit  segment: %7i\n", segment[LITSEG].imageUsed );
  837.     printf( "bss  segment: %7i\n", segment[BSSSEG].imageUsed );
  838.     printf( "instruction count: %i\n", instructionCount );
  839.     if ( errorCount != 0 ) {
  840.         printf( "Not writing a file due to errors\n" );
  841.         return;
  842.     }
  843.  
  844.     header.vmMagic = VM_MAGIC;
  845.     header.instructionCount = instructionCount;
  846.     header.codeOffset = sizeof( header );
  847.     header.codeLength = segment[CODESEG].imageUsed;
  848.     header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed;
  849.     header.dataLength = segment[DATASEG].imageUsed;
  850.     header.litLength = segment[LITSEG].imageUsed;
  851.     header.bssLength = segment[BSSSEG].imageUsed;
  852.  
  853.     printf( "Writing to %s\n", imageName );
  854.  
  855.     CreatePath( imageName );
  856.     f = SafeOpenWrite( imageName );
  857.     SafeWrite( f, &header, sizeof( header ) );
  858.     SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed );
  859.     SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed );
  860.     SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed );
  861.     fclose( f );
  862. }
  863.  
  864. /*
  865. ===============
  866. Assemble
  867. ===============
  868. */
  869. void Assemble( void ) {
  870.     int        i;
  871.     char    filename[MAX_OS_PATH];
  872.     char        *ptr;
  873.  
  874.     printf( "outputFilename: %s\n", outputFilename );
  875.  
  876.     for ( i = 0 ; i < numAsmFiles ; i++ ) {
  877.         strcpy( filename, asmFileNames[ i ] );
  878.         DefaultExtension( filename, ".asm" );
  879.         LoadFile( filename, &asmFiles[i] );
  880.     }
  881.  
  882.     // assemble
  883.     for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) {
  884.         segment[LITSEG].segmentBase = segment[DATASEG].imageUsed;
  885.         segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed;
  886.         for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
  887.             segment[i].imageUsed = 0;
  888.         }
  889.         segment[DATASEG].imageUsed = 4;        // skip the 0 byte, so NULL pointers are fixed up properly
  890.         instructionCount = 0;
  891.  
  892.         for ( i = 0 ; i < numAsmFiles ; i++ ) {
  893.             currentFileIndex = i;
  894.             currentFileName = asmFileNames[ i ];
  895.             currentFileLine = 0;
  896.             printf("pass %i: %s\n", passNumber, currentFileName );
  897.             ptr = asmFiles[i];
  898.             while ( ptr ) {
  899.                 ptr = ExtractLine( ptr );
  900.                 AssembleLine();
  901.             }
  902.         }
  903.  
  904.         // align all segment
  905.         for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
  906.             segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3;
  907.         }
  908.     }
  909.  
  910.     // reserve the stack in bss
  911.     DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed );
  912.     segment[BSSSEG].imageUsed += stackSize;
  913.     DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed );
  914.  
  915.     // write the image
  916.     WriteVmFile();
  917.  
  918.     // write the map file even if there were errors
  919.     WriteMapFile();
  920. }
  921.  
  922.  
  923. /*
  924. =============
  925. ParseOptionFile
  926.  
  927. =============
  928. */
  929. void ParseOptionFile( const char *filename ) {
  930.     char        expanded[MAX_OS_PATH];
  931.     char        *text, *text_p;
  932.  
  933.     strcpy( expanded, filename );
  934.     DefaultExtension( expanded, ".q3asm" );
  935.     LoadFile( expanded, &text );
  936.     if ( !text ) {
  937.         return;
  938.     }
  939.  
  940.     text_p = text;
  941.  
  942.     while( ( text_p = COM_Parse( text_p ) ) != 0 ) {
  943.         if ( !strcmp( com_token, "-o" ) ) {
  944.             // allow output override in option file
  945.             text_p = COM_Parse( text_p );
  946.             if ( text_p ) {
  947.                 strcpy( outputFilename, com_token );
  948.             }
  949.             continue;
  950.         }
  951.  
  952.         asmFileNames[ numAsmFiles ] = copystring( com_token );
  953.         numAsmFiles++;
  954.     }
  955. }
  956.  
  957. /*
  958. ==============
  959. main
  960. ==============
  961. */
  962. int main( int argc, char **argv ) {
  963.     int            i;
  964.     double        start, end;
  965.  
  966. //    _chdir( "/quake3/jccode/cgame/lccout" );    // hack for vc profiler
  967.  
  968.     if ( argc < 2 ) {
  969.         Error( "usage: q3asm [-o output] <files> or q3asm -f <listfile>\n" );
  970.     }
  971.  
  972.     start = I_FloatTime ();
  973.     InitTables();
  974.  
  975.     // default filename is "q3asm"
  976.     strcpy( outputFilename, "q3asm" );
  977.     numAsmFiles = 0;    
  978.  
  979.     for ( i = 1 ; i < argc ; i++ ) {
  980.         if ( argv[i][0] != '-' ) {
  981.             break;
  982.         }
  983.         if ( !strcmp( argv[i], "-o" ) ) {
  984.             if ( i == argc - 1 ) {
  985.                 Error( "-o must preceed a filename" );
  986.             }
  987.             strcpy( outputFilename, argv[i] );
  988.             i++;
  989.             continue;
  990.         }
  991.  
  992.         if ( !strcmp( argv[i], "-f" ) ) {
  993.             if ( i == argc - 1 ) {
  994.                 Error( "-f must preceed a filename" );
  995.             }
  996.             ParseOptionFile( argv[ i+1 ] );
  997.             i++;
  998.             continue;
  999.         }
  1000.         Error( "Unknown option: %s", argv[i] );
  1001.     }
  1002.  
  1003.     // the rest of the command line args are asm files
  1004.     for ( ; i < argc ; i++ ) {
  1005.         asmFileNames[ numAsmFiles ] = copystring( argv[ i ] );
  1006.         numAsmFiles++;
  1007.     }
  1008.  
  1009.     Assemble();
  1010.  
  1011.     end = I_FloatTime ();
  1012.     printf ("%5.0f seconds elapsed\n", end-start);
  1013.  
  1014.     return 0;
  1015. }
  1016.  
  1017.